# SEE model data package for new datasets
library(ranger)
library(tidyverse)         # for graphing and data cleaning
library(tidymodels)        # for modeling
library(stacks)            # for stacking models
library(naniar)            # for examining missing values (NAs)
library(lubridate)         # for date manipulation
library(moderndive)        # for King County housing data
library(vip)               # for variable importance plots
library(DALEX)             # for model interpretation  
library(DALEXtra)          # for extension of DALEX
library(patchwork)         # for combining plots nicely
library(kknn)
theme_set(theme_minimal()) # Lisa's favorite theme
data("lending_club")
# Data dictionary (as close as I could find): https://www.kaggle.com/wordsforthewise/lending-club/discussion/170691

When you finish the assignment, remove the # from the options chunk at the top, so that messages and warnings aren’t printed. If you are getting errors in your code, add error = TRUE so that the file knits. I would recommend not removing the # until you are completely finished.

Put it on GitHub!

From now on, GitHub should be part of your routine when doing assignments. I recommend making it part of your process anytime you are working in R, but I’ll make you show it’s part of your process for assignments.

Task: When you are finished with the assignment, post a link below to the GitHub repo for the assignment. If you want to post it to your personal website, that’s ok (not required). Make sure the link goes to a spot in the repo where I can easily find this assignment. For example, if you have a website with a blog and post the assignment as a blog post, link to the post’s folder in the repo. As an example, I’ve linked to my GitHub stacking material here.

Jackson’s Link: https://github.com/jacksontak/assignment2

Modeling

Before jumping into these problems, you should read through (and follow along with!) the model stacking and global model interpretation tutorials on the Course Materials tab of the course website.

We’ll be using the lending_club dataset from the modeldata library, which is part of tidymodels. The data dictionary they reference doesn’t seem to exist anymore, but it seems the one on this kaggle discussion is pretty close. It might also help to read a bit about Lending Club before starting in on the exercises.

The outcome we are interested in predicting is Class. And according to the dataset’s help page, its values are “either ‘good’ (meaning that the loan was fully paid back or currently on-time) or ‘bad’ (charged off, defaulted, of 21-120 days late)”.

Tasks: I will be expanding these, but this gives a good outline.

1. Explore the data, concentrating on examining distributions of variables and examining missing values.

# quantitative variables distributions
lending_club %>% 
  select(where(is.numeric)) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") %>% 
  ggplot(aes(x = value)) +
  geom_histogram(bins = 30) +
  facet_wrap(vars(variable), 
             scales = "free")

Based on the distribution plots above, we find that a lot of variables are heavily right-skewed. This suggests that we will have to log the variables instead of just interpreting the variables themselves. We also find some outliers in annual_inc and int_rate variables.

# Categorical variables 
lending_club %>% 
  select(where(is.factor)) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", 
               values_to = "value") %>% 
  ggplot(aes(x = value)) +
  geom_bar() +
  facet_wrap(vars(variable), 
             scales = "free")

For the categorical variables, we see that a lot of observations come from certain states than the others. We also find that there are much more good observations in class than the other. For the emp_length variable, there is one outlier as we see from the plot.

# examination of missing variables
lending_club %>% 
  add_n_miss() %>% 
  count(n_miss_all)
colSums(is.na(lending_club))
##                funded_amnt                       term 
##                          0                          0 
##                   int_rate                  sub_grade 
##                          0                          0 
##                 addr_state        verification_status 
##                          0                          0 
##                 annual_inc                 emp_length 
##                          0                          0 
##                delinq_2yrs             inq_last_6mths 
##                          0                          0 
##                 revol_util             acc_now_delinq 
##                          0                          0 
##                 open_il_6m                open_il_12m 
##                          0                          0 
##                open_il_24m               total_bal_il 
##                          0                          0 
##                   all_util                     inq_fi 
##                          0                          0 
##               inq_last_12m                delinq_amnt 
##                          0                          0 
##                  num_il_tl total_il_high_credit_limit 
##                          0                          0 
##                      Class 
##                          0

There seems to be no missing variables in our data set.

2. Do any data cleaning steps that need to happen before the model is build. For example, you might remove any variables that mean the same thing as the response variable (not sure if that happens here), get rid of rows where all variables have missing values, etc.

Be sure to add more “bad” Classes. This is not the best solution, but it will work for now. (Should investigate how to appropriately use step_sample_up() function from themis).

create_more_bad <- lending_club %>% 
  filter(Class == "bad") %>% 
  sample_n(size = 3000, replace = TRUE)

lending_club_mod <- lending_club %>% 
  bind_rows(create_more_bad) %>% 
  # remove zero variance and near zero variance
  select(-delinq_amnt, -acc_now_delinq)
  1. Split the data into training and test, putting 75% in the training data.
set.seed(494) # for reproducibility

# split the data into training and test
lc_split <- initial_split(lending_club_mod, 
                             prop = .75)
lc_training <- training(lc_split)
lc_testing <- testing(lc_split)

4. Set up the recipe and the pre-processing steps to build a lasso model. Some steps you should take:

  • Make all integer variables numeric (I’d highly recommend using step_mutate_at() or this will be a lot of code). We’ll want to do this for the model interpretation we’ll do later.
  • Think about grouping factor variables with many levels.
  • Make categorical variables dummy variables (make sure NOT to do this to the outcome variable).
  • Normalize quantitative variables.
lc_recipe <- recipe(Class ~ ., 
                    data = lc_training) %>% 
  
  # make all variables numeric
  step_mutate_at(all_numeric(), fn = ~as.numeric(.)) %>%
  
  # Group factor variables with many levels: there aren't any  
  
  # normalise quantitative variables
  # make all categorical into dummy variables
  step_dummy(all_nominal(),-all_outcomes()) %>% 
  step_normalize(all_predictors(), 
                 -all_nominal(), 
                 -has_role(match = "evaluative")) 
lc_recipe %>% 
    prep(lc_training) %>%
    juice()

5. Set up the lasso model and workflow. We will tune the penalty parameter.

# Set up LASSO Model
LC_lasso_mod <- 
  logistic_reg(mixture = 1) %>% 
  set_engine("glmnet") %>% 
  set_args(penalty = tune()) %>% 
  set_mode("classification")
# Set up work flow
LC_lasso_wf <- 
  workflow() %>% 
  add_recipe(lc_recipe) %>% 
  add_model(LC_lasso_mod)

6. Set up the model tuning for the penalty parameter. Be sure to add the control_stack_grid() for the control argument so we can use these results later when we stack. Find the accuracy and area under the roc curve for the model with the best tuning parameter. Use 5-fold cv.

set.seed(494) #for reproducible 5-fold

lending_cv <- vfold_cv(lc_training, v = 5)

# set penalty grid
penalty_grid <- grid_regular(penalty(),
                             levels = 10)

# add control grid by using the control_stack_grid function 
ctrl_grid <- control_stack_grid()

# metric set up 
# metric <- metric_set(accuracy)

# set up the model tuning for the penalty parameter
LC_lasso_tune <- 
  LC_lasso_wf %>% 
  tune_grid(
    resamples = lending_cv,
    grid = penalty_grid,
#    metrics = metric,
    control = ctrl_grid
    )
# choose the best tuning parameter 
best_param <- LC_lasso_tune %>% 
  select_best(metric = "accuracy")

# finalise workflow
LC_lasso_final_wf <- LC_lasso_wf %>% 
  finalize_workflow(best_param)

# fit final model
LC_lasso_final_mod <- LC_lasso_final_wf %>% 
  fit(data = lc_training)
LC_lasso_test <- LC_lasso_final_mod %>% 
  last_fit(lc_split)

LC_lasso_test %>% 
  collect_metrics()
  • The estimate for accuracy: 0.7548227

  • The area under the ROC curve: 0.7655311

7. Set up the recipe and the pre-processing steps to build a random forest model. You shouldn’t have to do as many steps. The only step you should need to do is making all integers numeric.

# set up random forest recipe
lc_recipe_2 <- recipe(Class ~ ., 
                    data = lc_training) %>% 
  
  # make all variables numeric
  step_mutate_at(all_numeric(), fn = ~as.numeric(.))

8. Set up the random forest model and workflow. We will tune the mtry and min_n parameters and set the number of trees, trees, to 100 (otherwise the next steps take too long).

# define rf model
lc_rf <-   rand_forest(mtry = tune(), 
                         min_n = tune(), 
                         trees = 100) %>% 
  set_mode("classification") %>% 
  set_engine("ranger")

# set workflow 
lc_rf_workflow <- 
  workflow() %>% 
  add_recipe(lc_recipe_2) %>% 
  add_model(lc_rf) 

9. Set up the model tuning for both the mtry and min_n parameters. Be sure to add the control_stack_grid() for the control argument so we can use these results later when we stack. Use only 3 levels in the grid. For the mtry parameter, you need to put finalize(mtry(), lending_training %>% select(-Class)) in as an argument instead of just mtry(), where lending_training is the name of your training data. This is because the mtry() grid will otherwise have unknowns in it. This part can take a while to run.

# penalty grid for rf
rf_penalty_grid <- grid_regular(finalize(mtry(), lc_training %>% 
    select(-Class)), min_n(), levels = 3)

# set up the model tuning for the penalty parameter
LC_rf_tune <- 
  lc_rf_workflow %>% 
  tune_grid(
    resamples = lending_cv,
    grid = rf_penalty_grid,
#    metrics = metric,
    control = ctrl_grid
    )

10. Find the best tuning parameters. What is the are the accuracy and area under the ROC curve for the model with those tuning parameters?

# choose the best tuning parameter 
best_param <- LC_rf_tune %>% 
  select_best(metric = "accuracy")

# finalise workflow
LC_rf_final_wf <- lc_rf_workflow %>% 
  finalize_workflow(best_param)

# fit final model
LC_rf_final_mod <- LC_rf_final_wf %>% 
  fit(data = lc_training)
LC_rf_test <- LC_rf_final_mod %>% 
  last_fit(lc_split)

LC_rf_test %>% 
  collect_metrics()
  • Accuracy: 0.9968886

  • Area under the ROC curve: 0.9999838

11. Use functions from the DALEX and DALEXtra libraries to create a histogram and boxplot of the residuals from the training data. How do they look? Any interesting behavior?

# lasso model explainer 
lasso_explain <- 
  explain_tidymodels(
    model = LC_lasso_final_mod,
    data = lc_training %>% select(-Class), 
    y = as.numeric(lc_training %>% pull(Class)),
    label = "lasso"
  )
## Preparation of a new explainer is initiated
##   -> model label       :  lasso 
##   -> data              :  9643  rows  20  cols 
##   -> data              :  tibble converted into a data.frame 
##   -> target variable   :  9643  values 
##   -> predict function  :  yhat.workflow  will be used (  default  )
##   -> predicted values  :  No value for predict function target column. (  default  )
##   -> model_info        :  package tidymodels , ver. 0.1.2 , task classification (  default  ) 
##   -> predicted values  :  numerical, min =  0.009290592 , mean =  0.7258119 , max =  0.9995852  
##   -> residual function :  difference between y and yhat (  default  )
##   -> residuals         :  numerical, min =  0.02700782 , mean =  0.9999996 , max =  1.990709  
##   A new explainer has been created! 
# random forest explainer
rf_explain <- 
  explain_tidymodels(
    model = LC_rf_final_mod,
    data = lc_training %>% select(-Class), 
    y = as.numeric(lc_training %>% pull(Class)),
    label = "rf"
  )
## Preparation of a new explainer is initiated
##   -> model label       :  rf 
##   -> data              :  9643  rows  20  cols 
##   -> data              :  tibble converted into a data.frame 
##   -> target variable   :  9643  values 
##   -> predict function  :  yhat.workflow  will be used (  default  )
##   -> predicted values  :  No value for predict function target column. (  default  )
##   -> model_info        :  package tidymodels , ver. 0.1.2 , task classification (  default  ) 
##   -> predicted values  :  numerical, min =  0 , mean =  0.703108 , max =  1  
##   -> residual function :  difference between y and yhat (  default  )
##   -> residuals         :  numerical, min =  0.595 , mean =  1.022704 , max =  1.28  
##   A new explainer has been created! 
lasso_mod_perf <- model_performance(lasso_explain)
rf_mod_perf <-  model_performance(rf_explain)
# lasso performance 
lasso_mod_perf  
## Measures for:  classification
## recall     : 0.6350227 
## precision  : 1 
## f1         : 0.7767754 
## accuracy   : 0.6350227 
## auc        : 0.4536517
## 
## Residuals:
##         0%        10%        20%        30%        40%        50%        60% 
## 0.02700782 0.34226738 0.54658494 1.01722637 1.06001963 1.10075602 1.14788191 
##        70%        80%        90%       100% 
## 1.20580261 1.29921064 1.42613664 1.99070941
# random forest performance
rf_mod_perf
## Measures for:  classification
## recall     : 0 
## precision  : NaN 
## f1         : NaN 
## accuracy   : 0 
## auc        : 0.4205624
## 
## Residuals:
##    0%   10%   20%   30%   40%   50%   60%   70%   80%   90%  100% 
## 0.595 0.990 1.000 1.000 1.000 1.010 1.020 1.030 1.050 1.080 1.280
hist_plot <- 
  plot(lasso_mod_perf,
       rf_mod_perf, 
       geom = "histogram")

box_plot <-
  plot(lasso_mod_perf,
       rf_mod_perf, 
       geom = "boxplot")

hist_plot + box_plot

Based on the visualisations above, we find that most of the residual observations from the training data are 1. [Explain more]

12. Use DALEX functions to create a variable importance plot from this model. What are the most important variables?

set.seed(494) #since we are sampling & permuting, we set a seed so we can replicate the results

# Lasso variable importance 
lasso_var_imp <- 
  model_parts(
    lasso_explain
    )

plot(lasso_var_imp, show_boxplots = TRUE)

set.seed(494)

# rf important variables
rf_var_imp <- 
  model_parts(
    rf_explain
    )

plot(rf_var_imp, show_boxplots = TRUE)

13. Write a function called cp_profile to make a CP profile. The function will take an explainer, a new observation, and a variable name as its arguments and create a CP profile for a quantitative predictor variable. You will need to use the predict_profile() function inside the function you create - put the variable name there so the plotting part is easier. You’ll also want to use aes_string() rather than aes() and quote the variables. Use the cp_profile() function to create one CP profile of your choosing. Be sure to choose a variable that is numeric, not integer. There seem to be issues with those that I’m looking into.

For an extra challenge, write a function that will work for either a quantitative or categorical variable.

If you need help with function writing check out the Functions chapter of R4DS by Wickham and Grolemund.

cp_profile <- function(explainer, obs, var) {
  predprof <- predict_profile(explainer = explainer,
                             new_observation = obs, 
                             variable = var) %>%
    rename(yhat = `_yhat_`) %>%
    ggplot(aes_string(x = var, y = "yhat")) +
           geom_line() 
} 

# observation 
obs4 <- lc_training %>% 
  slice(4)

# for LASSO
cp_profile(lasso_explain, obs4, "int_rate")

# for RF
cp_profile(rf_explain, obs4, "int_rate")

14. Use DALEX functions to create partial dependence plots (with the CP profiles in gray) for the 3-4 most important variables. If the important variables are categorical, you can instead make a CP profile for 3 observations in the dataset and discuss how you could go about constructing a partial dependence plot for a categorical variable (you don’t have to code it, but you can if you want an extra challenge). If it ever gives you an error that says, “Error: Can’t convert from VARIABLE to VARIABLE due to loss of precision”, then remove that variable from the list. I seem to have figured out why it’s doing that, but I don’t know how to fix it yet.

set.seed(494)

# For LASSO
partdepLASSO <- model_profile(explainer = lasso_explain, variables = c("addr_state", "int_rate", "open_il_12m"))

plot(partdepLASSO, 
     geom = "profiles")

# For RF 
partdepRF <- model_profile(explainer = rf_explain, variables = c("open_il_12m", "int_rate", "revol_util"))

plot(partdepRF, 
     geom = "profiles")

15. Fit one more model type of your choosing that will feed into the stacking model.

# KNN model 
knn <-
  nearest_neighbor(
    neighbors = tune("k")
  ) %>%
  set_engine("kknn") %>% 
  set_mode("classification")

# create the workflow
knn_wf <- 
  workflow() %>% 
  add_model(knn) %>%
  add_recipe(lc_recipe)

# tune it using 4 tuning parameters
knn_tune <- 
  knn_wf %>% 
  tune_grid(
    lending_cv,
#    metrics = metric,
    grid = 4,
    control = control_stack_grid()
  )

16. Create a model stack with the candidate models from the previous parts of the exercise and use the blend_predictions() function to find the coefficients of the stacked model. Create a plot examining the performance metrics for the different penalty parameters to assure you have captured the best one. If not, adjust the penalty. (HINT: use the autoplot() function). Which models are contributing most?

LC_stack <- 
  stacks() %>% 
  add_candidates(LC_rf_tune) %>% 
  add_candidates(LC_lasso_tune) %>% 
  add_candidates(knn_tune)
LC_blend <- 
  LC_stack%>% 
  blend_predictions() 

autoplot(LC_blend)

  1. Fit the final stacked model using fit_members(). Apply the model to the test data and report the accuracy and area under the curve. Create a graph of the ROC and construct a confusion matrix. Comment on what you see. Save this final model using the saveRDS() function - see the Use the model section of the tidymodels intro. We are going to use the model in the next part. You’ll want to save it in the folder where you create your shiny app.
# fit final stacked model 
LC_final_stacked <- LC_blend %>% 
  fit_members()
# apply to the test data
LC_final_stacked %>% 
  predict(new_data = lc_testing) %>% 
  bind_cols(lc_testing)
autoplot(LC_final_stacked)

Shiny app

If you are new to Shiny apps or it’s been awhile since you’ve made one, visit the Shiny links on our course Resource page. I would recommend starting with my resource because it will be the most basic. You won’t be doing anything super fancy in this app.

Everyone should watch the Theming Shiny talk by Carson Sievert so you can make your app look amazing.

Tasks:

You are going to create an app that allows a user to explore how the predicted probability of a loan being paid back (or maybe just the predicted class - either “good” or “bad”) changes depending on the values of the predictor variables.

Specifically, you will do the following:

  • Set up a separate project and GitHub repo for this app. Make sure the saved model from the previous problem is also in that folder. The app needs to be created in a file called exactly app.R that is also in the project folder.
  • At the top of the file, load any libraries you use in the app.
  • Use the readRDS() function to load the model.
  • You may want to load some of the data to use
  • Create a user interface (using the various *Input() functions) where someone could enter values for each variable that feeds into the model. You will want to think hard about which types of *Input() functions to use. Think about how you can best prevent mistakes (eg. entering free text could lead to many mistakes).
  • Another part of the user interface will allow them to choose a variable (you can limit this to only the quantitative variables) where they can explore the effects of changing that variable, holding all others constant.
  • After the user has entered all the required values, the output will be a CP profile with the the predicted value for the data that was entered, indicated by a point. I don’t think the functions from DALEX and DALEXtra will work with a stacked model, so you’ll likely have to (get to) do some of your own coding.
  • Use the bslib to theme your shiny app!
  • Publish your app to shinyapps.io. There are instructions for doing that on the tutorial I linked to above.
  • Write a paragraph or two describing your app on your website! Link to the app and your GitHub repository in your post. Include a link to your post here.

For this question, Lisa told us that we could just have an outline of how we would approach different parts in the shinyapp. Please find the general outline below:

outline

When creating this app, I would like to test one input at a time and make sure that it works.

Step 1: Define user interface (buttons, sliders, etc)

Read in the training data.

Pull function: pulls a variable out of the data set. Sort of like a select but select ends up with a dataset. Pull will create a vector rather.

One thing that I can think of is adding a submit button, which ensures that the graph changes only when I click the button.

Step 2: Adding static plots

To test if everything works until this point, I would love to add a static plot. In other words, we are not yet going to interact with the inputs.

We will be using the plotOutput function

Step 3: adding interaction

If the previous step works, we will build an interactive plot that changes as we change our inputs.

Step 4: choose variable

We will then decide what variables we can choose. In order to do this, we might need to convert into unquoted variable using this function: .data[[]]

Step 5: Add theme

We can then finally choose a theme of our choice.




Coded Bias

Watch the Code Bias film and write a short reflection. If you want some prompts, reflect on: What part of the film impacted you the most? Was there a part that surprised you and why? What emotions did you experience while watching?

REMEMBER TO ADD YOUR GITHUB LINK AT THE TOP OF THE PAGE AND UNCOMMENT THE knitr OPTIONS.

What part of the film impacted you the most?

I was kind of felt chills when Cathy was defining Machine learning. She says that Machine Learning is a scoring system that scores the probability of what you are about to do. She said there exists asymmetrical power and that it’s all about who owns the code. There are people suffering algorithmic harm and yet there is no appeal system. This mainly happens because we think of AI as the one-solution to all and stop thinking about potential problems. Therefore, as data scientists, it is important to check and think of all potential biases that could arise.

Another interesting part of the film is that Machine Learning is still a black box to programmers. We still do not understand why and how certain algorithms produce that result, which could eventually bite us in the long run. Microsoft’s Tay AI was also very impactful because it again showed how machine learning could go terribly wrong if AI is incorrectly used.

Was there a part that surprised you and why?

Below are parts that surprised me:

In the UK, through the freedom of information campaign, they found that 98% of matches are incorrectly matching an innocent person as a wanted person. Also, when a man with his face partially covered got arrested, that was surprising to see how the government entities have been using AI incorrectly and against human rights.

Amazon resume: women applications were all rejected. This shows that the machine was simply replicating the world as it exists and we are not making any social progress. This shows that when we build our models we need to be mindful of what world we are trying to use.

When the teacher that used to earn every recognition was labeled as a bad teacher by the algorithm.

Usually, technology advocates are opposed to regulations. However, it was really interesting and surprising to see how some were for regulation. For example, Cathy was saying that the power of these machine learning tools are left unregulated and We need laws because no recourse.

What emotions did you experience while watching?

As I was watching this film, I was slightly shocked, intimidated, and scared. Until now, technology was always a friendly tool for me that have made my life easier. However, through this video, I learned that these benefits come with costs and some people are affected more severely than others. It is very important to consider these problems and develop right regulations to ensure everyone is being treated somewhat fairly. I also leaned that we are being watched 24/7 and that technology provides and governments are not being transparent about it.

LS0tDQp0aXRsZTogJ0Fzc2lnbm1lbnQgMicNCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpDQpgYGANCg0KYGBge3J9DQojIFNFRSBtb2RlbCBkYXRhIHBhY2thZ2UgZm9yIG5ldyBkYXRhc2V0cw0KbGlicmFyeShyYW5nZXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICAgICAjIGZvciBncmFwaGluZyBhbmQgZGF0YSBjbGVhbmluZw0KbGlicmFyeSh0aWR5bW9kZWxzKSAgICAgICAgIyBmb3IgbW9kZWxpbmcNCmxpYnJhcnkoc3RhY2tzKSAgICAgICAgICAgICMgZm9yIHN0YWNraW5nIG1vZGVscw0KbGlicmFyeShuYW5pYXIpICAgICAgICAgICAgIyBmb3IgZXhhbWluaW5nIG1pc3NpbmcgdmFsdWVzIChOQXMpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkgICAgICAgICAjIGZvciBkYXRlIG1hbmlwdWxhdGlvbg0KbGlicmFyeShtb2Rlcm5kaXZlKSAgICAgICAgIyBmb3IgS2luZyBDb3VudHkgaG91c2luZyBkYXRhDQpsaWJyYXJ5KHZpcCkgICAgICAgICAgICAgICAjIGZvciB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3RzDQpsaWJyYXJ5KERBTEVYKSAgICAgICAgICAgICAjIGZvciBtb2RlbCBpbnRlcnByZXRhdGlvbiAgDQpsaWJyYXJ5KERBTEVYdHJhKSAgICAgICAgICAjIGZvciBleHRlbnNpb24gb2YgREFMRVgNCmxpYnJhcnkocGF0Y2h3b3JrKSAgICAgICAgICMgZm9yIGNvbWJpbmluZyBwbG90cyBuaWNlbHkNCmxpYnJhcnkoa2tubikNCnRoZW1lX3NldCh0aGVtZV9taW5pbWFsKCkpICMgTGlzYSdzIGZhdm9yaXRlIHRoZW1lDQpgYGANCg0KYGBge3IgZGF0YX0NCmRhdGEoImxlbmRpbmdfY2x1YiIpDQojIERhdGEgZGljdGlvbmFyeSAoYXMgY2xvc2UgYXMgSSBjb3VsZCBmaW5kKTogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS93b3Jkc2ZvcnRoZXdpc2UvbGVuZGluZy1jbHViL2Rpc2N1c3Npb24vMTcwNjkxDQpgYGANCg0KDQpXaGVuIHlvdSBmaW5pc2ggdGhlIGFzc2lnbm1lbnQsIHJlbW92ZSB0aGUgYCNgIGZyb20gdGhlIG9wdGlvbnMgY2h1bmsgYXQgdGhlIHRvcCwgc28gdGhhdCBtZXNzYWdlcyBhbmQgd2FybmluZ3MgYXJlbid0IHByaW50ZWQuIElmIHlvdSBhcmUgZ2V0dGluZyBlcnJvcnMgaW4geW91ciBjb2RlLCBhZGQgYGVycm9yID0gVFJVRWAgc28gdGhhdCB0aGUgZmlsZSBrbml0cy4gSSB3b3VsZCByZWNvbW1lbmQgbm90IHJlbW92aW5nIHRoZSBgI2AgdW50aWwgeW91IGFyZSBjb21wbGV0ZWx5IGZpbmlzaGVkLg0KDQojIyBQdXQgaXQgb24gR2l0SHViISAgICAgICAgDQoNCkZyb20gbm93IG9uLCBHaXRIdWIgc2hvdWxkIGJlIHBhcnQgb2YgeW91ciByb3V0aW5lIHdoZW4gZG9pbmcgYXNzaWdubWVudHMuIEkgcmVjb21tZW5kIG1ha2luZyBpdCBwYXJ0IG9mIHlvdXIgcHJvY2VzcyBhbnl0aW1lIHlvdSBhcmUgd29ya2luZyBpbiBSLCBidXQgSSdsbCBtYWtlIHlvdSBzaG93IGl0J3MgcGFydCBvZiB5b3VyIHByb2Nlc3MgZm9yIGFzc2lnbm1lbnRzLg0KDQoqKlRhc2sqKjogV2hlbiB5b3UgYXJlIGZpbmlzaGVkIHdpdGggdGhlIGFzc2lnbm1lbnQsIHBvc3QgYSBsaW5rIGJlbG93IHRvIHRoZSBHaXRIdWIgcmVwbyBmb3IgdGhlIGFzc2lnbm1lbnQuIElmIHlvdSB3YW50IHRvIHBvc3QgaXQgdG8geW91ciBwZXJzb25hbCB3ZWJzaXRlLCB0aGF0J3Mgb2sgKG5vdCByZXF1aXJlZCkuIE1ha2Ugc3VyZSB0aGUgbGluayBnb2VzIHRvIGEgc3BvdCBpbiB0aGUgcmVwbyB3aGVyZSBJIGNhbiBlYXNpbHkgZmluZCB0aGlzIGFzc2lnbm1lbnQuIEZvciBleGFtcGxlLCBpZiB5b3UgaGF2ZSBhIHdlYnNpdGUgd2l0aCBhIGJsb2cgYW5kIHBvc3QgdGhlIGFzc2lnbm1lbnQgYXMgYSBibG9nIHBvc3QsIGxpbmsgdG8gdGhlIHBvc3QncyBmb2xkZXIgaW4gdGhlIHJlcG8uIEFzIGFuIGV4YW1wbGUsIEkndmUgbGlua2VkIHRvIG15IEdpdEh1YiBzdGFja2luZyBtYXRlcmlhbCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2xsZW5kd2F5L2Fkc193ZWJzaXRlL3RyZWUvbWFzdGVyL19wb3N0cy8yMDIxLTAzLTIyLXN0YWNraW5nKS4NCg0KSmFja3NvbidzIExpbms6IGh0dHBzOi8vZ2l0aHViLmNvbS9qYWNrc29udGFrL2Fzc2lnbm1lbnQyDQoNCiMjIE1vZGVsaW5nDQoNCkJlZm9yZSBqdW1waW5nIGludG8gdGhlc2UgcHJvYmxlbXMsIHlvdSBzaG91bGQgcmVhZCB0aHJvdWdoIChhbmQgZm9sbG93IGFsb25nIHdpdGghKSB0aGUgW21vZGVsIHN0YWNraW5nXShodHRwczovL2FkdmFuY2VkLWRzLWluLXIubmV0bGlmeS5hcHAvcG9zdHMvMjAyMS0wMy0yMi1zdGFja2luZy8pIGFuZCBbZ2xvYmFsIG1vZGVsIGludGVycHJldGF0aW9uXShodHRwczovL2FkdmFuY2VkLWRzLWluLXIubmV0bGlmeS5hcHAvcG9zdHMvMjAyMS0wMy0yNC1pbWxnbG9iYWwvKSB0dXRvcmlhbHMgb24gdGhlIENvdXJzZSBNYXRlcmlhbHMgdGFiIG9mIHRoZSBjb3Vyc2Ugd2Vic2l0ZS4NCg0KV2UnbGwgYmUgdXNpbmcgdGhlIGBsZW5kaW5nX2NsdWJgIGRhdGFzZXQgZnJvbSB0aGUgYG1vZGVsZGF0YWAgbGlicmFyeSwgd2hpY2ggaXMgcGFydCBvZiBgdGlkeW1vZGVsc2AuIFRoZSBkYXRhIGRpY3Rpb25hcnkgdGhleSByZWZlcmVuY2UgZG9lc24ndCBzZWVtIHRvIGV4aXN0IGFueW1vcmUsIGJ1dCBpdCBzZWVtcyB0aGUgb25lIG9uIHRoaXMgW2thZ2dsZSBkaXNjdXNzaW9uXShodHRwczovL3d3dy5rYWdnbGUuY29tL3dvcmRzZm9ydGhld2lzZS9sZW5kaW5nLWNsdWIvZGlzY3Vzc2lvbi8xNzA2OTEpIGlzIHByZXR0eSBjbG9zZS4gSXQgbWlnaHQgYWxzbyBoZWxwIHRvIHJlYWQgYSBiaXQgYWJvdXQgW0xlbmRpbmcgQ2x1Yl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGVuZGluZ0NsdWIpIGJlZm9yZSBzdGFydGluZyBpbiBvbiB0aGUgZXhlcmNpc2VzLg0KDQpUaGUgb3V0Y29tZSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBwcmVkaWN0aW5nIGlzIGBDbGFzc2AuIEFuZCBhY2NvcmRpbmcgdG8gdGhlIGRhdGFzZXQncyBoZWxwIHBhZ2UsIGl0cyB2YWx1ZXMgYXJlICJlaXRoZXIgJ2dvb2QnIChtZWFuaW5nIHRoYXQgdGhlIGxvYW4gd2FzIGZ1bGx5IHBhaWQgYmFjayBvciBjdXJyZW50bHkgb24tdGltZSkgb3IgJ2JhZCcgKGNoYXJnZWQgb2ZmLCBkZWZhdWx0ZWQsIG9mIDIxLTEyMCBkYXlzIGxhdGUpIi4NCg0KKipUYXNrczoqKiBJIHdpbGwgYmUgZXhwYW5kaW5nIHRoZXNlLCBidXQgdGhpcyBnaXZlcyBhIGdvb2Qgb3V0bGluZS4NCg0KKjEuIEV4cGxvcmUgdGhlIGRhdGEsIGNvbmNlbnRyYXRpbmcgb24gZXhhbWluaW5nIGRpc3RyaWJ1dGlvbnMgb2YgdmFyaWFibGVzIGFuZCBleGFtaW5pbmcgbWlzc2luZyB2YWx1ZXMuKiANCg0KYGBge3J9DQojIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMgZGlzdHJpYnV0aW9ucw0KbGVuZGluZ19jbHViICU+JSANCiAgc2VsZWN0KHdoZXJlKGlzLm51bWVyaWMpKSAlPiUgDQogIHBpdm90X2xvbmdlcihjb2xzID0gZXZlcnl0aGluZygpLA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAidmFyaWFibGUiLCANCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkgKw0KICBmYWNldF93cmFwKHZhcnModmFyaWFibGUpLCANCiAgICAgICAgICAgICBzY2FsZXMgPSAiZnJlZSIpDQpgYGANCg0KQmFzZWQgb24gdGhlIGRpc3RyaWJ1dGlvbiBwbG90cyBhYm92ZSwgd2UgZmluZCB0aGF0IGEgbG90IG9mIHZhcmlhYmxlcyBhcmUgaGVhdmlseSByaWdodC1za2V3ZWQuIFRoaXMgc3VnZ2VzdHMgdGhhdCB3ZSB3aWxsIGhhdmUgdG8gbG9nIHRoZSB2YXJpYWJsZXMgaW5zdGVhZCBvZiBqdXN0IGludGVycHJldGluZyB0aGUgdmFyaWFibGVzIHRoZW1zZWx2ZXMuIFdlIGFsc28gZmluZCBzb21lIG91dGxpZXJzIGluIGBhbm51YWxfaW5jYCBhbmQgYGludF9yYXRlYCB2YXJpYWJsZXMuIA0KDQoNCmBgYHtyfQ0KIyBDYXRlZ29yaWNhbCB2YXJpYWJsZXMgDQpsZW5kaW5nX2NsdWIgJT4lIA0KICBzZWxlY3Qod2hlcmUoaXMuZmFjdG9yKSkgJT4lIA0KICBwaXZvdF9sb25nZXIoY29scyA9IGV2ZXJ5dGhpbmcoKSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwgDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWUiKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHZhbHVlKSkgKw0KICBnZW9tX2JhcigpICsNCiAgZmFjZXRfd3JhcCh2YXJzKHZhcmlhYmxlKSwgDQogICAgICAgICAgICAgc2NhbGVzID0gImZyZWUiKQ0KYGBgDQoNCkZvciB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGVzLCB3ZSBzZWUgdGhhdCBhIGxvdCBvZiBvYnNlcnZhdGlvbnMgY29tZSBmcm9tIGNlcnRhaW4gc3RhdGVzIHRoYW4gdGhlIG90aGVycy4gV2UgYWxzbyBmaW5kIHRoYXQgdGhlcmUgYXJlIG11Y2ggbW9yZSBnb29kIG9ic2VydmF0aW9ucyBpbiBgY2xhc3NgIHRoYW4gdGhlIG90aGVyLiBGb3IgdGhlIGBlbXBfbGVuZ3RoYCB2YXJpYWJsZSwgdGhlcmUgaXMgb25lIG91dGxpZXIgYXMgd2Ugc2VlIGZyb20gdGhlIHBsb3QuIA0KDQoNCmBgYHtyfQ0KIyBleGFtaW5hdGlvbiBvZiBtaXNzaW5nIHZhcmlhYmxlcw0KbGVuZGluZ19jbHViICU+JSANCiAgYWRkX25fbWlzcygpICU+JSANCiAgY291bnQobl9taXNzX2FsbCkNCg0KY29sU3Vtcyhpcy5uYShsZW5kaW5nX2NsdWIpKQ0KYGBgDQoNClRoZXJlIHNlZW1zIHRvIGJlIG5vIG1pc3NpbmcgdmFyaWFibGVzIGluIG91ciBkYXRhIHNldC4gDQoNCg0KKjIuIERvIGFueSBkYXRhIGNsZWFuaW5nIHN0ZXBzIHRoYXQgbmVlZCB0byBoYXBwZW4gYmVmb3JlIHRoZSBtb2RlbCBpcyBidWlsZC4gRm9yIGV4YW1wbGUsIHlvdSBtaWdodCByZW1vdmUgYW55IHZhcmlhYmxlcyB0aGF0IG1lYW4gdGhlIHNhbWUgdGhpbmcgYXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIChub3Qgc3VyZSBpZiB0aGF0IGhhcHBlbnMgaGVyZSksIGdldCByaWQgb2Ygcm93cyB3aGVyZSBhbGwgdmFyaWFibGVzIGhhdmUgbWlzc2luZyB2YWx1ZXMsIGV0Yy4qIA0KDQpCZSBzdXJlIHRvIGFkZCBtb3JlICJiYWQiIENsYXNzZXMuIFRoaXMgaXMgbm90IHRoZSBiZXN0IHNvbHV0aW9uLCBidXQgaXQgd2lsbCB3b3JrIGZvciBub3cuIChTaG91bGQgaW52ZXN0aWdhdGUgaG93IHRvIGFwcHJvcHJpYXRlbHkgdXNlIGBzdGVwX3NhbXBsZV91cCgpYCBmdW5jdGlvbiBmcm9tIFtgdGhlbWlzYF0oaHR0cHM6Ly9naXRodWIuY29tL3RpZHltb2RlbHMvdGhlbWlzKSkuDQoNCmBgYHtyfQ0KY3JlYXRlX21vcmVfYmFkIDwtIGxlbmRpbmdfY2x1YiAlPiUgDQogIGZpbHRlcihDbGFzcyA9PSAiYmFkIikgJT4lIA0KICBzYW1wbGVfbihzaXplID0gMzAwMCwgcmVwbGFjZSA9IFRSVUUpDQoNCmxlbmRpbmdfY2x1Yl9tb2QgPC0gbGVuZGluZ19jbHViICU+JSANCiAgYmluZF9yb3dzKGNyZWF0ZV9tb3JlX2JhZCkgJT4lIA0KICAjIHJlbW92ZSB6ZXJvIHZhcmlhbmNlIGFuZCBuZWFyIHplcm8gdmFyaWFuY2UNCiAgc2VsZWN0KC1kZWxpbnFfYW1udCwgLWFjY19ub3dfZGVsaW5xKQ0KYGBgDQoNCg0KDQozLiBTcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0LCBwdXR0aW5nIDc1XCUgaW4gdGhlIHRyYWluaW5nIGRhdGEuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNDk0KSAjIGZvciByZXByb2R1Y2liaWxpdHkNCg0KIyBzcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0DQpsY19zcGxpdCA8LSBpbml0aWFsX3NwbGl0KGxlbmRpbmdfY2x1Yl9tb2QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9wID0gLjc1KQ0KbGNfdHJhaW5pbmcgPC0gdHJhaW5pbmcobGNfc3BsaXQpDQpsY190ZXN0aW5nIDwtIHRlc3RpbmcobGNfc3BsaXQpDQpgYGANCg0KKjQuIFNldCB1cCB0aGUgcmVjaXBlIGFuZCB0aGUgcHJlLXByb2Nlc3Npbmcgc3RlcHMgdG8gYnVpbGQgYSBsYXNzbyBtb2RlbC4gU29tZSBzdGVwcyB5b3Ugc2hvdWxkIHRha2U6Kg0KDQoqIE1ha2UgYWxsIGludGVnZXIgdmFyaWFibGVzIG51bWVyaWMgKEknZCBoaWdobHkgcmVjb21tZW5kIHVzaW5nIGBzdGVwX211dGF0ZV9hdCgpYCBvciB0aGlzIHdpbGwgYmUgYSBsb3Qgb2YgY29kZSkuIFdlJ2xsIHdhbnQgdG8gZG8gdGhpcyBmb3IgdGhlIG1vZGVsIGludGVycHJldGF0aW9uIHdlJ2xsIGRvIGxhdGVyLiAgDQoqIFRoaW5rIGFib3V0IGdyb3VwaW5nIGZhY3RvciB2YXJpYWJsZXMgd2l0aCBtYW55IGxldmVscy4gIA0KKiBNYWtlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBkdW1teSB2YXJpYWJsZXMgKG1ha2Ugc3VyZSBOT1QgdG8gZG8gdGhpcyB0byB0aGUgb3V0Y29tZSB2YXJpYWJsZSkuICANCiogTm9ybWFsaXplIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMuICANCg0KYGBge3J9DQpsY19yZWNpcGUgPC0gcmVjaXBlKENsYXNzIH4gLiwgDQogICAgICAgICAgICAgICAgICAgIGRhdGEgPSBsY190cmFpbmluZykgJT4lIA0KICANCiAgIyBtYWtlIGFsbCB2YXJpYWJsZXMgbnVtZXJpYw0KICBzdGVwX211dGF0ZV9hdChhbGxfbnVtZXJpYygpLCBmbiA9IH5hcy5udW1lcmljKC4pKSAlPiUNCiAgDQogICMgR3JvdXAgZmFjdG9yIHZhcmlhYmxlcyB3aXRoIG1hbnkgbGV2ZWxzOiB0aGVyZSBhcmVuJ3QgYW55ICANCiAgDQogICMgbm9ybWFsaXNlIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMNCiAgIyBtYWtlIGFsbCBjYXRlZ29yaWNhbCBpbnRvIGR1bW15IHZhcmlhYmxlcw0KICBzdGVwX2R1bW15KGFsbF9ub21pbmFsKCksLWFsbF9vdXRjb21lcygpKSAlPiUgDQogIHN0ZXBfbm9ybWFsaXplKGFsbF9wcmVkaWN0b3JzKCksIA0KICAgICAgICAgICAgICAgICAtYWxsX25vbWluYWwoKSwgDQogICAgICAgICAgICAgICAgIC1oYXNfcm9sZShtYXRjaCA9ICJldmFsdWF0aXZlIikpIA0KYGBgDQoNCmBgYHtyfQ0KbGNfcmVjaXBlICU+JSANCiAgICBwcmVwKGxjX3RyYWluaW5nKSAlPiUNCiAgICBqdWljZSgpDQpgYGANCg0KDQoqNS4gU2V0IHVwIHRoZSBsYXNzbyBtb2RlbCBhbmQgd29ya2Zsb3cuIFdlIHdpbGwgdHVuZSB0aGUgYHBlbmFsdHlgIHBhcmFtZXRlci4qDQoNCmBgYHtyfQ0KIyBTZXQgdXAgTEFTU08gTW9kZWwNCkxDX2xhc3NvX21vZCA8LSANCiAgbG9naXN0aWNfcmVnKG1peHR1cmUgPSAxKSAlPiUgDQogIHNldF9lbmdpbmUoImdsbW5ldCIpICU+JSANCiAgc2V0X2FyZ3MocGVuYWx0eSA9IHR1bmUoKSkgJT4lIA0KICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTZXQgdXAgd29yayBmbG93DQpMQ19sYXNzb193ZiA8LSANCiAgd29ya2Zsb3coKSAlPiUgDQogIGFkZF9yZWNpcGUobGNfcmVjaXBlKSAlPiUgDQogIGFkZF9tb2RlbChMQ19sYXNzb19tb2QpDQpgYGANCg0KDQoNCio2LiBTZXQgdXAgdGhlIG1vZGVsIHR1bmluZyBmb3IgdGhlIGBwZW5hbHR5YCBwYXJhbWV0ZXIuIEJlIHN1cmUgdG8gYWRkIHRoZSBgY29udHJvbF9zdGFja19ncmlkKClgIGZvciB0aGUgYGNvbnRyb2xgIGFyZ3VtZW50IHNvIHdlIGNhbiB1c2UgdGhlc2UgcmVzdWx0cyBsYXRlciB3aGVuIHdlIHN0YWNrLiBGaW5kIHRoZSBhY2N1cmFjeSBhbmQgYXJlYSB1bmRlciB0aGUgcm9jIGN1cnZlIGZvciB0aGUgbW9kZWwgd2l0aCB0aGUgYmVzdCB0dW5pbmcgcGFyYW1ldGVyLiAgVXNlIDUtZm9sZCBjdi4qDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNDk0KSAjZm9yIHJlcHJvZHVjaWJsZSA1LWZvbGQNCg0KbGVuZGluZ19jdiA8LSB2Zm9sZF9jdihsY190cmFpbmluZywgdiA9IDUpDQoNCiMgc2V0IHBlbmFsdHkgZ3JpZA0KcGVuYWx0eV9ncmlkIDwtIGdyaWRfcmVndWxhcihwZW5hbHR5KCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IDEwKQ0KDQojIGFkZCBjb250cm9sIGdyaWQgYnkgdXNpbmcgdGhlIGNvbnRyb2xfc3RhY2tfZ3JpZCBmdW5jdGlvbiANCmN0cmxfZ3JpZCA8LSBjb250cm9sX3N0YWNrX2dyaWQoKQ0KDQojIG1ldHJpYyBzZXQgdXAgDQojIG1ldHJpYyA8LSBtZXRyaWNfc2V0KGFjY3VyYWN5KQ0KDQojIHNldCB1cCB0aGUgbW9kZWwgdHVuaW5nIGZvciB0aGUgcGVuYWx0eSBwYXJhbWV0ZXINCkxDX2xhc3NvX3R1bmUgPC0gDQogIExDX2xhc3NvX3dmICU+JSANCiAgdHVuZV9ncmlkKA0KICAgIHJlc2FtcGxlcyA9IGxlbmRpbmdfY3YsDQogICAgZ3JpZCA9IHBlbmFsdHlfZ3JpZCwNCiMgICAgbWV0cmljcyA9IG1ldHJpYywNCiAgICBjb250cm9sID0gY3RybF9ncmlkDQogICAgKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjaG9vc2UgdGhlIGJlc3QgdHVuaW5nIHBhcmFtZXRlciANCmJlc3RfcGFyYW0gPC0gTENfbGFzc29fdHVuZSAlPiUgDQogIHNlbGVjdF9iZXN0KG1ldHJpYyA9ICJhY2N1cmFjeSIpDQoNCiMgZmluYWxpc2Ugd29ya2Zsb3cNCkxDX2xhc3NvX2ZpbmFsX3dmIDwtIExDX2xhc3NvX3dmICU+JSANCiAgZmluYWxpemVfd29ya2Zsb3coYmVzdF9wYXJhbSkNCg0KIyBmaXQgZmluYWwgbW9kZWwNCkxDX2xhc3NvX2ZpbmFsX21vZCA8LSBMQ19sYXNzb19maW5hbF93ZiAlPiUgDQogIGZpdChkYXRhID0gbGNfdHJhaW5pbmcpDQpgYGANCg0KYGBge3J9DQpMQ19sYXNzb190ZXN0IDwtIExDX2xhc3NvX2ZpbmFsX21vZCAlPiUgDQogIGxhc3RfZml0KGxjX3NwbGl0KQ0KDQpMQ19sYXNzb190ZXN0ICU+JSANCiAgY29sbGVjdF9tZXRyaWNzKCkNCmBgYA0KDQotIFRoZSBlc3RpbWF0ZSBmb3IgYWNjdXJhY3k6IDAuNzU0ODIyNw0KDQotIFRoZSBhcmVhIHVuZGVyIHRoZSBST0MgY3VydmU6IDAuNzY1NTMxMSANCg0KDQoNCio3LiBTZXQgdXAgdGhlIHJlY2lwZSBhbmQgdGhlIHByZS1wcm9jZXNzaW5nIHN0ZXBzIHRvIGJ1aWxkIGEgcmFuZG9tIGZvcmVzdCBtb2RlbC4gWW91IHNob3VsZG4ndCBoYXZlIHRvIGRvIGFzIG1hbnkgc3RlcHMuIFRoZSBvbmx5IHN0ZXAgeW91IHNob3VsZCBuZWVkIHRvIGRvIGlzIG1ha2luZyBhbGwgaW50ZWdlcnMgbnVtZXJpYy4qDQoNCmBgYHtyfQ0KIyBzZXQgdXAgcmFuZG9tIGZvcmVzdCByZWNpcGUNCmxjX3JlY2lwZV8yIDwtIHJlY2lwZShDbGFzcyB+IC4sIA0KICAgICAgICAgICAgICAgICAgICBkYXRhID0gbGNfdHJhaW5pbmcpICU+JSANCiAgDQogICMgbWFrZSBhbGwgdmFyaWFibGVzIG51bWVyaWMNCiAgc3RlcF9tdXRhdGVfYXQoYWxsX251bWVyaWMoKSwgZm4gPSB+YXMubnVtZXJpYyguKSkNCmBgYA0KDQoNCio4LiBTZXQgdXAgdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwgYW5kIHdvcmtmbG93LiBXZSB3aWxsIHR1bmUgdGhlIGBtdHJ5YCBhbmQgYG1pbl9uYCBwYXJhbWV0ZXJzIGFuZCBzZXQgdGhlIG51bWJlciBvZiB0cmVlcywgYHRyZWVzYCwgdG8gMTAwIChvdGhlcndpc2UgdGhlIG5leHQgc3RlcHMgdGFrZSB0b28gbG9uZykuKg0KDQpgYGB7cn0NCiMgZGVmaW5lIHJmIG1vZGVsDQpsY19yZiA8LSAgIHJhbmRfZm9yZXN0KG10cnkgPSB0dW5lKCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIG1pbl9uID0gdHVuZSgpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICB0cmVlcyA9IDEwMCkgJT4lIA0KICBzZXRfbW9kZSgiY2xhc3NpZmljYXRpb24iKSAlPiUgDQogIHNldF9lbmdpbmUoInJhbmdlciIpDQoNCiMgc2V0IHdvcmtmbG93IA0KbGNfcmZfd29ya2Zsb3cgPC0gDQogIHdvcmtmbG93KCkgJT4lIA0KICBhZGRfcmVjaXBlKGxjX3JlY2lwZV8yKSAlPiUgDQogIGFkZF9tb2RlbChsY19yZikgDQpgYGANCg0KDQoqOS4gU2V0IHVwIHRoZSBtb2RlbCB0dW5pbmcgZm9yIGJvdGggdGhlIGBtdHJ5YCBhbmQgYG1pbl9uYCBwYXJhbWV0ZXJzLiBCZSBzdXJlIHRvIGFkZCB0aGUgYGNvbnRyb2xfc3RhY2tfZ3JpZCgpYCBmb3IgdGhlIGBjb250cm9sYCBhcmd1bWVudCBzbyB3ZSBjYW4gdXNlIHRoZXNlIHJlc3VsdHMgbGF0ZXIgd2hlbiB3ZSBzdGFjay4gVXNlIG9ubHkgMyBsZXZlbHMgaW4gdGhlIGdyaWQuIEZvciB0aGUgYG10cnlgIHBhcmFtZXRlciwgeW91IG5lZWQgdG8gcHV0IGBmaW5hbGl6ZShtdHJ5KCksIGxlbmRpbmdfdHJhaW5pbmcgJT4lIHNlbGVjdCgtQ2xhc3MpKWAgaW4gYXMgYW4gYXJndW1lbnQgaW5zdGVhZCBvZiBqdXN0IGBtdHJ5KClgLCB3aGVyZSBgbGVuZGluZ190cmFpbmluZ2AgaXMgdGhlIG5hbWUgb2YgeW91ciB0cmFpbmluZyBkYXRhLiBUaGlzIGlzIGJlY2F1c2UgdGhlIGBtdHJ5KClgIGdyaWQgd2lsbCBvdGhlcndpc2UgaGF2ZSB1bmtub3ducyBpbiBpdC4gVGhpcyBwYXJ0IGNhbiB0YWtlIGEgd2hpbGUgdG8gcnVuLioNCg0KYGBge3J9DQojIHBlbmFsdHkgZ3JpZCBmb3IgcmYNCnJmX3BlbmFsdHlfZ3JpZCA8LSBncmlkX3JlZ3VsYXIoZmluYWxpemUobXRyeSgpLCBsY190cmFpbmluZyAlPiUgDQogICAgc2VsZWN0KC1DbGFzcykpLCBtaW5fbigpLCBsZXZlbHMgPSAzKQ0KDQojIHNldCB1cCB0aGUgbW9kZWwgdHVuaW5nIGZvciB0aGUgcGVuYWx0eSBwYXJhbWV0ZXINCkxDX3JmX3R1bmUgPC0gDQogIGxjX3JmX3dvcmtmbG93ICU+JSANCiAgdHVuZV9ncmlkKA0KICAgIHJlc2FtcGxlcyA9IGxlbmRpbmdfY3YsDQogICAgZ3JpZCA9IHJmX3BlbmFsdHlfZ3JpZCwNCiMgICAgbWV0cmljcyA9IG1ldHJpYywNCiAgICBjb250cm9sID0gY3RybF9ncmlkDQogICAgKQ0KYGBgDQoNCg0KKjEwLiBGaW5kIHRoZSBiZXN0IHR1bmluZyBwYXJhbWV0ZXJzLiBXaGF0IGlzIHRoZSBhcmUgdGhlIGFjY3VyYWN5IGFuZCBhcmVhIHVuZGVyIHRoZSBST0MgY3VydmUgZm9yIHRoZSBtb2RlbCB3aXRoIHRob3NlIHR1bmluZyBwYXJhbWV0ZXJzPyoNCg0KYGBge3J9DQojIGNob29zZSB0aGUgYmVzdCB0dW5pbmcgcGFyYW1ldGVyIA0KYmVzdF9wYXJhbSA8LSBMQ19yZl90dW5lICU+JSANCiAgc2VsZWN0X2Jlc3QobWV0cmljID0gImFjY3VyYWN5IikNCg0KIyBmaW5hbGlzZSB3b3JrZmxvdw0KTENfcmZfZmluYWxfd2YgPC0gbGNfcmZfd29ya2Zsb3cgJT4lIA0KICBmaW5hbGl6ZV93b3JrZmxvdyhiZXN0X3BhcmFtKQ0KDQojIGZpdCBmaW5hbCBtb2RlbA0KTENfcmZfZmluYWxfbW9kIDwtIExDX3JmX2ZpbmFsX3dmICU+JSANCiAgZml0KGRhdGEgPSBsY190cmFpbmluZykNCmBgYA0KDQpgYGB7cn0NCkxDX3JmX3Rlc3QgPC0gTENfcmZfZmluYWxfbW9kICU+JSANCiAgbGFzdF9maXQobGNfc3BsaXQpDQoNCkxDX3JmX3Rlc3QgJT4lIA0KICBjb2xsZWN0X21ldHJpY3MoKQ0KYGBgDQoNCi0gQWNjdXJhY3k6IDAuOTk2ODg4Ng0KDQotIEFyZWEgdW5kZXIgdGhlIFJPQyBjdXJ2ZTogMC45OTk5ODM4DQoNCg0KKjExLiBVc2UgZnVuY3Rpb25zIGZyb20gdGhlIGBEQUxFWGAgYW5kIGBEQUxFWHRyYWAgbGlicmFyaWVzIHRvIGNyZWF0ZSBhIGhpc3RvZ3JhbSBhbmQgYm94cGxvdCBvZiB0aGUgcmVzaWR1YWxzIGZyb20gdGhlIHRyYWluaW5nIGRhdGEuIEhvdyBkbyB0aGV5IGxvb2s/IEFueSBpbnRlcmVzdGluZyBiZWhhdmlvcj8qDQoNCmBgYHtyfQ0KIyBsYXNzbyBtb2RlbCBleHBsYWluZXIgDQpsYXNzb19leHBsYWluIDwtIA0KICBleHBsYWluX3RpZHltb2RlbHMoDQogICAgbW9kZWwgPSBMQ19sYXNzb19maW5hbF9tb2QsDQogICAgZGF0YSA9IGxjX3RyYWluaW5nICU+JSBzZWxlY3QoLUNsYXNzKSwgDQogICAgeSA9IGFzLm51bWVyaWMobGNfdHJhaW5pbmcgJT4lIHB1bGwoQ2xhc3MpKSwNCiAgICBsYWJlbCA9ICJsYXNzbyINCiAgKQ0KDQoNCiMgcmFuZG9tIGZvcmVzdCBleHBsYWluZXINCnJmX2V4cGxhaW4gPC0gDQogIGV4cGxhaW5fdGlkeW1vZGVscygNCiAgICBtb2RlbCA9IExDX3JmX2ZpbmFsX21vZCwNCiAgICBkYXRhID0gbGNfdHJhaW5pbmcgJT4lIHNlbGVjdCgtQ2xhc3MpLCANCiAgICB5ID0gYXMubnVtZXJpYyhsY190cmFpbmluZyAlPiUgcHVsbChDbGFzcykpLA0KICAgIGxhYmVsID0gInJmIg0KICApDQpgYGANCg0KYGBge3J9DQpsYXNzb19tb2RfcGVyZiA8LSBtb2RlbF9wZXJmb3JtYW5jZShsYXNzb19leHBsYWluKQ0KcmZfbW9kX3BlcmYgPC0gIG1vZGVsX3BlcmZvcm1hbmNlKHJmX2V4cGxhaW4pDQpgYGANCg0KYGBge3J9DQojIGxhc3NvIHBlcmZvcm1hbmNlIA0KbGFzc29fbW9kX3BlcmYgIA0KDQojIHJhbmRvbSBmb3Jlc3QgcGVyZm9ybWFuY2UNCnJmX21vZF9wZXJmDQpgYGANCmBgYHtyfQ0KaGlzdF9wbG90IDwtIA0KICBwbG90KGxhc3NvX21vZF9wZXJmLA0KICAgICAgIHJmX21vZF9wZXJmLCANCiAgICAgICBnZW9tID0gImhpc3RvZ3JhbSIpDQoNCmJveF9wbG90IDwtDQogIHBsb3QobGFzc29fbW9kX3BlcmYsDQogICAgICAgcmZfbW9kX3BlcmYsIA0KICAgICAgIGdlb20gPSAiYm94cGxvdCIpDQoNCmhpc3RfcGxvdCArIGJveF9wbG90DQpgYGANCg0KQmFzZWQgb24gdGhlIHZpc3VhbGlzYXRpb25zIGFib3ZlLCB3ZSBmaW5kIHRoYXQgbW9zdCBvZiB0aGUgcmVzaWR1YWwgb2JzZXJ2YXRpb25zIGZyb20gdGhlIHRyYWluaW5nIGRhdGEgYXJlIDEuIFtFeHBsYWluIG1vcmVdICANCg0KDQoqMTIuIFVzZSBgREFMRVhgIGZ1bmN0aW9ucyB0byBjcmVhdGUgYSB2YXJpYWJsZSBpbXBvcnRhbmNlIHBsb3QgZnJvbSB0aGlzIG1vZGVsLiBXaGF0IGFyZSB0aGUgbW9zdCBpbXBvcnRhbnQgdmFyaWFibGVzPyogDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoNDk0KSAjc2luY2Ugd2UgYXJlIHNhbXBsaW5nICYgcGVybXV0aW5nLCB3ZSBzZXQgYSBzZWVkIHNvIHdlIGNhbiByZXBsaWNhdGUgdGhlIHJlc3VsdHMNCg0KIyBMYXNzbyB2YXJpYWJsZSBpbXBvcnRhbmNlIA0KbGFzc29fdmFyX2ltcCA8LSANCiAgbW9kZWxfcGFydHMoDQogICAgbGFzc29fZXhwbGFpbg0KICAgICkNCg0KcGxvdChsYXNzb192YXJfaW1wLCBzaG93X2JveHBsb3RzID0gVFJVRSkNCmBgYA0KDQpgYGB7cn0NCnNldC5zZWVkKDQ5NCkNCg0KIyByZiBpbXBvcnRhbnQgdmFyaWFibGVzDQpyZl92YXJfaW1wIDwtIA0KICBtb2RlbF9wYXJ0cygNCiAgICByZl9leHBsYWluDQogICAgKQ0KDQpwbG90KHJmX3Zhcl9pbXAsIHNob3dfYm94cGxvdHMgPSBUUlVFKQ0KYGBgDQoNCg0KKjEzLiBXcml0ZSBhIGZ1bmN0aW9uIGNhbGxlZCBgY3BfcHJvZmlsZWAgdG8gbWFrZSBhIENQIHByb2ZpbGUuIFRoZSBmdW5jdGlvbiB3aWxsIHRha2UgYW4gZXhwbGFpbmVyLCBhIG5ldyBvYnNlcnZhdGlvbiwgYW5kIGEgdmFyaWFibGUgbmFtZSBhcyBpdHMgYXJndW1lbnRzIGFuZCBjcmVhdGUgYSBDUCBwcm9maWxlIGZvciBhIHF1YW50aXRhdGl2ZSBwcmVkaWN0b3IgdmFyaWFibGUuIFlvdSB3aWxsIG5lZWQgdG8gdXNlIHRoZSBgcHJlZGljdF9wcm9maWxlKClgIGZ1bmN0aW9uIGluc2lkZSB0aGUgZnVuY3Rpb24geW91IGNyZWF0ZSAtIHB1dCB0aGUgdmFyaWFibGUgbmFtZSB0aGVyZSBzbyB0aGUgcGxvdHRpbmcgcGFydCBpcyBlYXNpZXIuIFlvdSdsbCBhbHNvIHdhbnQgdG8gdXNlIGBhZXNfc3RyaW5nKClgIHJhdGhlciB0aGFuIGBhZXMoKWAgYW5kIHF1b3RlIHRoZSB2YXJpYWJsZXMuIFVzZSB0aGUgYGNwX3Byb2ZpbGUoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIG9uZSBDUCBwcm9maWxlIG9mIHlvdXIgY2hvb3NpbmcuIEJlIHN1cmUgdG8gY2hvb3NlIGEgdmFyaWFibGUgdGhhdCBpcyBudW1lcmljLCBub3QgaW50ZWdlci4gVGhlcmUgc2VlbSB0byBiZSBpc3N1ZXMgd2l0aCB0aG9zZSB0aGF0IEknbSBsb29raW5nIGludG8uKg0KDQoqRm9yIGFuIGV4dHJhIGNoYWxsZW5nZSwgd3JpdGUgYSBmdW5jdGlvbiB0aGF0IHdpbGwgd29yayBmb3IgZWl0aGVyIGEgcXVhbnRpdGF0aXZlIG9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlLiogDQoNCipJZiB5b3UgbmVlZCBoZWxwIHdpdGggZnVuY3Rpb24gd3JpdGluZyBjaGVjayBvdXQgdGhlIFtGdW5jdGlvbnNdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZnVuY3Rpb25zLmh0bWwpIGNoYXB0ZXIgb2YgUjREUyBieSBXaWNraGFtIGFuZCBHcm9sZW11bmQuKg0KDQoNCmBgYHtyfQ0KY3BfcHJvZmlsZSA8LSBmdW5jdGlvbihleHBsYWluZXIsIG9icywgdmFyKSB7DQogIHByZWRwcm9mIDwtIHByZWRpY3RfcHJvZmlsZShleHBsYWluZXIgPSBleHBsYWluZXIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ld19vYnNlcnZhdGlvbiA9IG9icywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID0gdmFyKSAlPiUNCiAgICByZW5hbWUoeWhhdCA9IGBfeWhhdF9gKSAlPiUNCiAgICBnZ3Bsb3QoYWVzX3N0cmluZyh4ID0gdmFyLCB5ID0gInloYXQiKSkgKw0KICAgICAgICAgICBnZW9tX2xpbmUoKSANCn0gDQoNCiMgb2JzZXJ2YXRpb24gDQpvYnM0IDwtIGxjX3RyYWluaW5nICU+JSANCiAgc2xpY2UoNCkNCg0KIyBmb3IgTEFTU08NCmNwX3Byb2ZpbGUobGFzc29fZXhwbGFpbiwgb2JzNCwgImludF9yYXRlIikNCg0KIyBmb3IgUkYNCmNwX3Byb2ZpbGUocmZfZXhwbGFpbiwgb2JzNCwgImludF9yYXRlIikNCmBgYA0KDQoNCg0KDQoqMTQuIFVzZSBgREFMRVhgIGZ1bmN0aW9ucyB0byBjcmVhdGUgcGFydGlhbCBkZXBlbmRlbmNlIHBsb3RzICh3aXRoIHRoZSBDUCBwcm9maWxlcyBpbiBncmF5KSBmb3IgdGhlIDMtNCBtb3N0IGltcG9ydGFudCB2YXJpYWJsZXMuIElmIHRoZSBpbXBvcnRhbnQgdmFyaWFibGVzIGFyZSBjYXRlZ29yaWNhbCwgeW91IGNhbiBpbnN0ZWFkIG1ha2UgYSBDUCBwcm9maWxlIGZvciAzIG9ic2VydmF0aW9ucyBpbiB0aGUgZGF0YXNldCBhbmQgZGlzY3VzcyBob3cgeW91IGNvdWxkIGdvIGFib3V0IGNvbnN0cnVjdGluZyBhIHBhcnRpYWwgZGVwZW5kZW5jZSBwbG90IGZvciBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlICh5b3UgZG9uJ3QgaGF2ZSB0byBjb2RlIGl0LCBidXQgeW91IGNhbiBpZiB5b3Ugd2FudCBhbiBleHRyYSBjaGFsbGVuZ2UpLiBJZiBpdCBldmVyIGdpdmVzIHlvdSBhbiBlcnJvciB0aGF0IHNheXMsICJFcnJvcjogQ2FuJ3QgY29udmVydCBmcm9tIGBWQVJJQUJMRWAgPGRvdWJsZT4gdG8gYFZBUklBQkxFYCA8aW50ZWdlcj4gZHVlIHRvIGxvc3Mgb2YgcHJlY2lzaW9uIiwgdGhlbiByZW1vdmUgdGhhdCB2YXJpYWJsZSBmcm9tIHRoZSBsaXN0LiBJIHNlZW0gdG8gaGF2ZSBmaWd1cmVkIG91dCB3aHkgaXQncyBkb2luZyB0aGF0LCBidXQgSSBkb24ndCBrbm93IGhvdyB0byBmaXggaXQgeWV0LioNCg0KYGBge3J9DQpzZXQuc2VlZCg0OTQpDQoNCiMgRm9yIExBU1NPDQpwYXJ0ZGVwTEFTU08gPC0gbW9kZWxfcHJvZmlsZShleHBsYWluZXIgPSBsYXNzb19leHBsYWluLCB2YXJpYWJsZXMgPSBjKCJhZGRyX3N0YXRlIiwgImludF9yYXRlIiwgIm9wZW5faWxfMTJtIikpDQoNCnBsb3QocGFydGRlcExBU1NPLCANCiAgICAgZ2VvbSA9ICJwcm9maWxlcyIpDQoNCiMgRm9yIFJGIA0KcGFydGRlcFJGIDwtIG1vZGVsX3Byb2ZpbGUoZXhwbGFpbmVyID0gcmZfZXhwbGFpbiwgdmFyaWFibGVzID0gYygib3Blbl9pbF8xMm0iLCAiaW50X3JhdGUiLCAicmV2b2xfdXRpbCIpKQ0KDQpwbG90KHBhcnRkZXBSRiwgDQogICAgIGdlb20gPSAicHJvZmlsZXMiKQ0KDQpgYGANCg0KDQoqMTUuIEZpdCBvbmUgbW9yZSBtb2RlbCB0eXBlIG9mIHlvdXIgY2hvb3NpbmcgdGhhdCB3aWxsIGZlZWQgaW50byB0aGUgc3RhY2tpbmcgbW9kZWwuKg0KDQpgYGB7cn0NCiMgS05OIG1vZGVsIA0Ka25uIDwtDQogIG5lYXJlc3RfbmVpZ2hib3IoDQogICAgbmVpZ2hib3JzID0gdHVuZSgiayIpDQogICkgJT4lDQogIHNldF9lbmdpbmUoImtrbm4iKSAlPiUgDQogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpDQoNCiMgY3JlYXRlIHRoZSB3b3JrZmxvdw0Ka25uX3dmIDwtIA0KICB3b3JrZmxvdygpICU+JSANCiAgYWRkX21vZGVsKGtubikgJT4lDQogIGFkZF9yZWNpcGUobGNfcmVjaXBlKQ0KDQojIHR1bmUgaXQgdXNpbmcgNCB0dW5pbmcgcGFyYW1ldGVycw0Ka25uX3R1bmUgPC0gDQogIGtubl93ZiAlPiUgDQogIHR1bmVfZ3JpZCgNCiAgICBsZW5kaW5nX2N2LA0KIyAgICBtZXRyaWNzID0gbWV0cmljLA0KICAgIGdyaWQgPSA0LA0KICAgIGNvbnRyb2wgPSBjb250cm9sX3N0YWNrX2dyaWQoKQ0KICApDQpgYGANCg0KDQoqKjE2LiBDcmVhdGUgYSBtb2RlbCBzdGFjayB3aXRoIHRoZSBjYW5kaWRhdGUgbW9kZWxzIGZyb20gdGhlIHByZXZpb3VzIHBhcnRzIG9mIHRoZSBleGVyY2lzZSBhbmQgdXNlIHRoZSBgYmxlbmRfcHJlZGljdGlvbnMoKWAgZnVuY3Rpb24gdG8gZmluZCB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBzdGFja2VkIG1vZGVsLiBDcmVhdGUgYSBwbG90IGV4YW1pbmluZyB0aGUgcGVyZm9ybWFuY2UgbWV0cmljcyBmb3IgdGhlIGRpZmZlcmVudCBwZW5hbHR5IHBhcmFtZXRlcnMgdG8gYXNzdXJlIHlvdSBoYXZlIGNhcHR1cmVkIHRoZSBiZXN0IG9uZS4gSWYgbm90LCBhZGp1c3QgdGhlIHBlbmFsdHkuIChISU5UOiB1c2UgdGhlIGBhdXRvcGxvdCgpYCBmdW5jdGlvbikuIFdoaWNoIG1vZGVscyBhcmUgY29udHJpYnV0aW5nIG1vc3Q/KioNCg0KYGBge3J9DQpMQ19zdGFjayA8LSANCiAgc3RhY2tzKCkgJT4lIA0KICBhZGRfY2FuZGlkYXRlcyhMQ19yZl90dW5lKSAlPiUgDQogIGFkZF9jYW5kaWRhdGVzKExDX2xhc3NvX3R1bmUpICU+JSANCiAgYWRkX2NhbmRpZGF0ZXMoa25uX3R1bmUpDQpgYGANCg0KYGBge3J9DQpMQ19ibGVuZCA8LSANCiAgTENfc3RhY2slPiUgDQogIGJsZW5kX3ByZWRpY3Rpb25zKCkgDQoNCmF1dG9wbG90KExDX2JsZW5kKQ0KYGBgDQoNCg0KMTcuIEZpdCB0aGUgZmluYWwgc3RhY2tlZCBtb2RlbCB1c2luZyBgZml0X21lbWJlcnMoKWAuIEFwcGx5IHRoZSBtb2RlbCB0byB0aGUgdGVzdCBkYXRhIGFuZCByZXBvcnQgdGhlIGFjY3VyYWN5IGFuZCBhcmVhIHVuZGVyIHRoZSBjdXJ2ZS4gQ3JlYXRlIGEgZ3JhcGggb2YgdGhlIFJPQyBhbmQgY29uc3RydWN0IGEgY29uZnVzaW9uIG1hdHJpeC4gQ29tbWVudCBvbiB3aGF0IHlvdSBzZWUuIFNhdmUgdGhpcyBmaW5hbCBtb2RlbCB1c2luZyB0aGUgYHNhdmVSRFMoKWAgZnVuY3Rpb24gLSBzZWUgdGhlIFtVc2UgdGhlIG1vZGVsXShodHRwczovL2FkdmFuY2VkLWRzLWluLXIubmV0bGlmeS5hcHAvcG9zdHMvMjAyMS0wMy0xNi1tbC1yZXZpZXcvI3VzZS10aGUtbW9kZWwpIHNlY3Rpb24gb2YgdGhlIGB0aWR5bW9kZWxzYCBpbnRyby4gV2UgYXJlIGdvaW5nIHRvIHVzZSB0aGUgbW9kZWwgaW4gdGhlIG5leHQgcGFydC4gWW91J2xsIHdhbnQgdG8gc2F2ZSBpdCBpbiB0aGUgZm9sZGVyIHdoZXJlIHlvdSBjcmVhdGUgeW91ciBzaGlueSBhcHAuDQoNCg0KYGBge3J9DQojIGZpdCBmaW5hbCBzdGFja2VkIG1vZGVsIA0KTENfZmluYWxfc3RhY2tlZCA8LSBMQ19ibGVuZCAlPiUgDQogIGZpdF9tZW1iZXJzKCkNCmBgYA0KDQpgYGB7cn0NCiMgYXBwbHkgdG8gdGhlIHRlc3QgZGF0YQ0KTENfZmluYWxfc3RhY2tlZCAlPiUgDQogIHByZWRpY3QobmV3X2RhdGEgPSBsY190ZXN0aW5nKSAlPiUgDQogIGJpbmRfY29scyhsY190ZXN0aW5nKQ0KYGBgDQoNCmBgYHtyfQ0KYXV0b3Bsb3QoTENfZmluYWxfc3RhY2tlZCkNCmBgYA0KDQoNCiMjIFNoaW55IGFwcA0KDQpJZiB5b3UgYXJlIG5ldyB0byBTaGlueSBhcHBzIG9yIGl0J3MgYmVlbiBhd2hpbGUgc2luY2UgeW91J3ZlIG1hZGUgb25lLCB2aXNpdCB0aGUgU2hpbnkgbGlua3Mgb24gb3VyIGNvdXJzZSBbUmVzb3VyY2VdKGh0dHBzOi8vYWR2YW5jZWQtZHMtaW4tci5uZXRsaWZ5LmFwcC9yZXNvdXJjZXMuaHRtbCkgcGFnZS4gSSB3b3VsZCByZWNvbW1lbmQgc3RhcnRpbmcgd2l0aCBteSByZXNvdXJjZSBiZWNhdXNlIGl0IHdpbGwgYmUgdGhlIG1vc3QgYmFzaWMuIFlvdSB3b24ndCBiZSBkb2luZyBhbnl0aGluZyBzdXBlciBmYW5jeSBpbiB0aGlzIGFwcC4gDQoNCkV2ZXJ5b25lIHNob3VsZCB3YXRjaCB0aGUgW1RoZW1pbmcgU2hpbnldKGh0dHBzOi8veW91dHUuYmUvYjlXV05PNFAyblkpIHRhbGsgYnkgQ2Fyc29uIFNpZXZlcnQgc28geW91IGNhbiBtYWtlIHlvdXIgYXBwIGxvb2sgYW1hemluZy4NCg0KKipUYXNrczoqKg0KDQpZb3UgYXJlIGdvaW5nIHRvIGNyZWF0ZSBhbiBhcHAgdGhhdCBhbGxvd3MgYSB1c2VyIHRvIGV4cGxvcmUgaG93IHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgb2YgYSBsb2FuIGJlaW5nIHBhaWQgYmFjayAob3IgbWF5YmUganVzdCB0aGUgcHJlZGljdGVkIGNsYXNzIC0gZWl0aGVyICJnb29kIiBvciAiYmFkIikgY2hhbmdlcyBkZXBlbmRpbmcgb24gdGhlIHZhbHVlcyBvZiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcy4NCg0KU3BlY2lmaWNhbGx5LCB5b3Ugd2lsbCBkbyB0aGUgZm9sbG93aW5nOg0KDQoqIFNldCB1cCBhIHNlcGFyYXRlIHByb2plY3QgYW5kIEdpdEh1YiByZXBvIGZvciB0aGlzIGFwcC4gTWFrZSBzdXJlIHRoZSBzYXZlZCBtb2RlbCBmcm9tIHRoZSBwcmV2aW91cyBwcm9ibGVtIGlzIGFsc28gaW4gdGhhdCBmb2xkZXIuIFRoZSBhcHAgbmVlZHMgdG8gYmUgY3JlYXRlZCBpbiBhIGZpbGUgY2FsbGVkICpleGFjdGx5KiBhcHAuUiB0aGF0IGlzIGFsc28gaW4gdGhlIHByb2plY3QgZm9sZGVyLiAgIA0KKiBBdCB0aGUgdG9wIG9mIHRoZSBmaWxlLCBsb2FkIGFueSBsaWJyYXJpZXMgeW91IHVzZSBpbiB0aGUgYXBwLiAgDQoqIFVzZSB0aGUgYHJlYWRSRFMoKWAgZnVuY3Rpb24gdG8gbG9hZCB0aGUgbW9kZWwuICANCiogWW91IG1heSB3YW50IHRvIGxvYWQgc29tZSBvZiB0aGUgZGF0YSB0byB1c2UNCiogQ3JlYXRlIGEgdXNlciBpbnRlcmZhY2UgKHVzaW5nIHRoZSB2YXJpb3VzIGAqSW5wdXQoKWAgZnVuY3Rpb25zKSB3aGVyZSBzb21lb25lIGNvdWxkIGVudGVyIHZhbHVlcyBmb3IgZWFjaCB2YXJpYWJsZSB0aGF0IGZlZWRzIGludG8gdGhlIG1vZGVsLiBZb3Ugd2lsbCB3YW50IHRvIHRoaW5rIGhhcmQgYWJvdXQgd2hpY2ggdHlwZXMgb2YgYCpJbnB1dCgpYCBmdW5jdGlvbnMgdG8gdXNlLiBUaGluayBhYm91dCBob3cgeW91IGNhbiBiZXN0IHByZXZlbnQgbWlzdGFrZXMgKGVnLiBlbnRlcmluZyBmcmVlIHRleHQgY291bGQgbGVhZCB0byBtYW55IG1pc3Rha2VzKS4gDQoqIEFub3RoZXIgcGFydCBvZiB0aGUgdXNlciBpbnRlcmZhY2Ugd2lsbCBhbGxvdyB0aGVtIHRvIGNob29zZSBhIHZhcmlhYmxlICh5b3UgY2FuIGxpbWl0IHRoaXMgdG8gb25seSB0aGUgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcykgd2hlcmUgdGhleSBjYW4gZXhwbG9yZSB0aGUgZWZmZWN0cyBvZiBjaGFuZ2luZyB0aGF0IHZhcmlhYmxlLCBob2xkaW5nIGFsbCBvdGhlcnMgY29uc3RhbnQuICANCiogQWZ0ZXIgdGhlIHVzZXIgaGFzIGVudGVyZWQgYWxsIHRoZSByZXF1aXJlZCB2YWx1ZXMsIHRoZSBvdXRwdXQgd2lsbCBiZSBhIENQIHByb2ZpbGUgd2l0aCB0aGUgdGhlIHByZWRpY3RlZCB2YWx1ZSBmb3IgdGhlIGRhdGEgdGhhdCB3YXMgZW50ZXJlZCwgaW5kaWNhdGVkIGJ5IGEgcG9pbnQuIEkgZG9uJ3QgdGhpbmsgdGhlIGZ1bmN0aW9ucyBmcm9tIGBEQUxFWGAgYW5kIGBEQUxFWHRyYWAgd2lsbCB3b3JrIHdpdGggYSBzdGFja2VkIG1vZGVsLCBzbyB5b3UnbGwgbGlrZWx5IGhhdmUgdG8gKGdldCB0bykgZG8gc29tZSBvZiB5b3VyIG93biBjb2RpbmcuIA0KKiBVc2UgdGhlIGBic2xpYmAgdG8gdGhlbWUgeW91ciBzaGlueSBhcHAhICANCiogUHVibGlzaCB5b3VyIGFwcCB0byBbc2hpbnlhcHBzLmlvXShodHRwczovL3d3dy5zaGlueWFwcHMuaW8vKS4gVGhlcmUgYXJlIGluc3RydWN0aW9ucyBmb3IgZG9pbmcgdGhhdCBvbiB0aGUgdHV0b3JpYWwgSSBsaW5rZWQgdG8gYWJvdmUuICAgDQoqIFdyaXRlIGEgcGFyYWdyYXBoIG9yIHR3byBkZXNjcmliaW5nIHlvdXIgYXBwIG9uIHlvdXIgd2Vic2l0ZSEgTGluayB0byB0aGUgYXBwIGFuZCB5b3VyIEdpdEh1YiByZXBvc2l0b3J5IGluIHlvdXIgcG9zdC4gSW5jbHVkZSBhIGxpbmsgdG8geW91ciBwb3N0IGhlcmUuIA0KDQoNCipGb3IgdGhpcyBxdWVzdGlvbiwgTGlzYSB0b2xkIHVzIHRoYXQgd2UgY291bGQganVzdCBoYXZlIGFuIG91dGxpbmUgb2YgaG93IHdlIHdvdWxkIGFwcHJvYWNoIGRpZmZlcmVudCBwYXJ0cyBpbiB0aGUgc2hpbnlhcHAuIFBsZWFzZSBmaW5kIHRoZSBnZW5lcmFsIG91dGxpbmUgYmVsb3c6Kg0KDQojIG91dGxpbmUgDQpXaGVuIGNyZWF0aW5nIHRoaXMgYXBwLCBJIHdvdWxkIGxpa2UgdG8gdGVzdCBvbmUgaW5wdXQgYXQgYSB0aW1lIGFuZCBtYWtlIHN1cmUgdGhhdCBpdCB3b3Jrcy4gDQoNCiMgU3RlcCAxOiBEZWZpbmUgdXNlciBpbnRlcmZhY2UgKGJ1dHRvbnMsIHNsaWRlcnMsIGV0YykNCg0KUmVhZCBpbiB0aGUgdHJhaW5pbmcgZGF0YS4gIA0KDQpQdWxsIGZ1bmN0aW9uOiBwdWxscyBhIHZhcmlhYmxlIG91dCBvZiB0aGUgZGF0YSBzZXQuIFNvcnQgb2YgbGlrZSBhIHNlbGVjdCBidXQgc2VsZWN0IGVuZHMgdXAgd2l0aCBhIGRhdGFzZXQuIFB1bGwgd2lsbCBjcmVhdGUgYSB2ZWN0b3IgcmF0aGVyLiANCg0KT25lIHRoaW5nIHRoYXQgSSBjYW4gdGhpbmsgb2YgaXMgYWRkaW5nIGEgc3VibWl0IGJ1dHRvbiwgd2hpY2ggZW5zdXJlcyB0aGF0IHRoZSBncmFwaCBjaGFuZ2VzIG9ubHkgd2hlbiBJIGNsaWNrIHRoZSBidXR0b24uIA0KDQoNCiMgU3RlcCAyOiBBZGRpbmcgc3RhdGljIHBsb3RzICANCg0KVG8gdGVzdCBpZiBldmVyeXRoaW5nIHdvcmtzIHVudGlsIHRoaXMgcG9pbnQsIEkgd291bGQgbG92ZSB0byBhZGQgYSBzdGF0aWMgcGxvdC4gSW4gb3RoZXIgd29yZHMsIHdlIGFyZSBub3QgeWV0IGdvaW5nIHRvIGludGVyYWN0IHdpdGggdGhlIGlucHV0cy4gDQoNCldlIHdpbGwgYmUgdXNpbmcgdGhlIGBwbG90T3V0cHV0YCBmdW5jdGlvbiANCg0KDQojIFN0ZXAgMzogYWRkaW5nIGludGVyYWN0aW9uIA0KDQpJZiB0aGUgcHJldmlvdXMgc3RlcCB3b3Jrcywgd2Ugd2lsbCBidWlsZCBhbiBpbnRlcmFjdGl2ZSBwbG90IHRoYXQgY2hhbmdlcyBhcyB3ZSBjaGFuZ2Ugb3VyIGlucHV0cy4gDQoNCg0KIyBTdGVwIDQ6IGNob29zZSB2YXJpYWJsZSANCg0KV2Ugd2lsbCB0aGVuIGRlY2lkZSB3aGF0IHZhcmlhYmxlcyB3ZSBjYW4gY2hvb3NlLiBJbiBvcmRlciB0byBkbyB0aGlzLCB3ZSBtaWdodCBuZWVkIHRvIGNvbnZlcnQgaW50byB1bnF1b3RlZCB2YXJpYWJsZSB1c2luZyB0aGlzIGZ1bmN0aW9uOiAuZGF0YVtbXV0NCg0KIyBTdGVwIDU6IEFkZCB0aGVtZQ0KDQpXZSBjYW4gdGhlbiBmaW5hbGx5IGNob29zZSBhIHRoZW1lIG9mIG91ciBjaG9pY2UuIA0KDQpcDQpcDQpcDQoNCg0KDQojIyBDb2RlZCBCaWFzDQoNCipXYXRjaCB0aGUgW0NvZGUgQmlhc10oaHR0cHM6Ly93d3cucGJzLm9yZy9pbmRlcGVuZGVudGxlbnMvZmlsbXMvY29kZWQtYmlhcy8pIGZpbG0gYW5kIHdyaXRlIGEgc2hvcnQgcmVmbGVjdGlvbi4gSWYgeW91IHdhbnQgc29tZSBwcm9tcHRzLCByZWZsZWN0IG9uOiBXaGF0IHBhcnQgb2YgdGhlIGZpbG0gaW1wYWN0ZWQgeW91IHRoZSBtb3N0PyBXYXMgdGhlcmUgYSBwYXJ0IHRoYXQgc3VycHJpc2VkIHlvdSBhbmQgd2h5PyBXaGF0IGVtb3Rpb25zIGRpZCB5b3UgZXhwZXJpZW5jZSB3aGlsZSB3YXRjaGluZz8qDQoNClJFTUVNQkVSIFRPIEFERCBZT1VSIEdJVEhVQiBMSU5LIEFUIFRIRSBUT1AgT0YgVEhFIFBBR0UgQU5EIFVOQ09NTUVOVCBUSEUgYGtuaXRyYCBPUFRJT05TLg0KDQoqV2hhdCBwYXJ0IG9mIHRoZSBmaWxtIGltcGFjdGVkIHlvdSB0aGUgbW9zdD8qIA0KDQpJIHdhcyBraW5kIG9mIGZlbHQgY2hpbGxzIHdoZW4gQ2F0aHkgd2FzIGRlZmluaW5nIE1hY2hpbmUgbGVhcm5pbmcuIFNoZSBzYXlzIHRoYXQgTWFjaGluZSBMZWFybmluZyBpcyBhIHNjb3Jpbmcgc3lzdGVtIHRoYXQgc2NvcmVzIHRoZSBwcm9iYWJpbGl0eSBvZiB3aGF0IHlvdSBhcmUgYWJvdXQgdG8gZG8uIFNoZSBzYWlkIHRoZXJlIGV4aXN0cyBhc3ltbWV0cmljYWwgcG93ZXIgYW5kIHRoYXQgaXQncyBhbGwgYWJvdXQgd2hvIG93bnMgdGhlIGNvZGUuIFRoZXJlIGFyZSBwZW9wbGUgc3VmZmVyaW5nIGFsZ29yaXRobWljIGhhcm0gYW5kIHlldCB0aGVyZSBpcyBubyBhcHBlYWwgc3lzdGVtLiBUaGlzIG1haW5seSBoYXBwZW5zIGJlY2F1c2Ugd2UgdGhpbmsgb2YgQUkgYXMgdGhlIG9uZS1zb2x1dGlvbiB0byBhbGwgYW5kIHN0b3AgdGhpbmtpbmcgYWJvdXQgcG90ZW50aWFsIHByb2JsZW1zLiBUaGVyZWZvcmUsIGFzIGRhdGEgc2NpZW50aXN0cywgaXQgaXMgaW1wb3J0YW50IHRvIGNoZWNrIGFuZCB0aGluayBvZiBhbGwgcG90ZW50aWFsIGJpYXNlcyB0aGF0IGNvdWxkIGFyaXNlLiAgDQoNCkFub3RoZXIgaW50ZXJlc3RpbmcgcGFydCBvZiB0aGUgZmlsbSBpcyB0aGF0IE1hY2hpbmUgTGVhcm5pbmcgaXMgc3RpbGwgYSBibGFjayBib3ggdG8gcHJvZ3JhbW1lcnMuIFdlIHN0aWxsIGRvIG5vdCB1bmRlcnN0YW5kIHdoeSBhbmQgaG93IGNlcnRhaW4gYWxnb3JpdGhtcyBwcm9kdWNlIHRoYXQgcmVzdWx0LCB3aGljaCBjb3VsZCBldmVudHVhbGx5IGJpdGUgdXMgaW4gdGhlIGxvbmcgcnVuLiBNaWNyb3NvZnQncyBUYXkgQUkgd2FzIGFsc28gdmVyeSBpbXBhY3RmdWwgYmVjYXVzZSBpdCBhZ2FpbiBzaG93ZWQgaG93IG1hY2hpbmUgbGVhcm5pbmcgY291bGQgZ28gdGVycmlibHkgd3JvbmcgaWYgQUkgaXMgaW5jb3JyZWN0bHkgdXNlZC4gIA0KDQoqV2FzIHRoZXJlIGEgcGFydCB0aGF0IHN1cnByaXNlZCB5b3UgYW5kIHdoeT8qDQoNCkJlbG93IGFyZSBwYXJ0cyB0aGF0IHN1cnByaXNlZCBtZTogDQoNCkluIHRoZSBVSywgdGhyb3VnaCB0aGUgZnJlZWRvbSBvZiBpbmZvcm1hdGlvbiBjYW1wYWlnbiwgdGhleSBmb3VuZCB0aGF0IDk4JSBvZiBtYXRjaGVzIGFyZSBpbmNvcnJlY3RseSBtYXRjaGluZyBhbiBpbm5vY2VudCBwZXJzb24gYXMgYSB3YW50ZWQgcGVyc29uLiBBbHNvLCB3aGVuIGEgbWFuIHdpdGggaGlzIGZhY2UgcGFydGlhbGx5IGNvdmVyZWQgZ290IGFycmVzdGVkLCB0aGF0IHdhcyBzdXJwcmlzaW5nIHRvIHNlZSBob3cgdGhlIGdvdmVybm1lbnQgZW50aXRpZXMgaGF2ZSBiZWVuIHVzaW5nIEFJIGluY29ycmVjdGx5IGFuZCBhZ2FpbnN0IGh1bWFuIHJpZ2h0cy4gIA0KDQpBbWF6b24gcmVzdW1lOiB3b21lbiBhcHBsaWNhdGlvbnMgd2VyZSBhbGwgcmVqZWN0ZWQuIFRoaXMgc2hvd3MgdGhhdCB0aGUgbWFjaGluZSB3YXMgc2ltcGx5IHJlcGxpY2F0aW5nIHRoZSB3b3JsZCBhcyBpdCBleGlzdHMgYW5kIHdlIGFyZSBub3QgbWFraW5nIGFueSBzb2NpYWwgcHJvZ3Jlc3MuIFRoaXMgc2hvd3MgdGhhdCB3aGVuIHdlIGJ1aWxkIG91ciBtb2RlbHMgd2UgbmVlZCB0byBiZSBtaW5kZnVsIG9mIHdoYXQgd29ybGQgd2UgYXJlIHRyeWluZyB0byB1c2UuICANCg0KV2hlbiB0aGUgdGVhY2hlciB0aGF0IHVzZWQgdG8gZWFybiBldmVyeSByZWNvZ25pdGlvbiB3YXMgbGFiZWxlZCBhcyBhIGJhZCB0ZWFjaGVyIGJ5IHRoZSBhbGdvcml0aG0uIA0KDQpVc3VhbGx5LCB0ZWNobm9sb2d5IGFkdm9jYXRlcyBhcmUgb3Bwb3NlZCB0byByZWd1bGF0aW9ucy4gSG93ZXZlciwgaXQgd2FzIHJlYWxseSBpbnRlcmVzdGluZyBhbmQgc3VycHJpc2luZyB0byBzZWUgaG93IHNvbWUgd2VyZSBmb3IgcmVndWxhdGlvbi4gRm9yIGV4YW1wbGUsIENhdGh5IHdhcyBzYXlpbmcgdGhhdCB0aGUgcG93ZXIgb2YgdGhlc2UgbWFjaGluZSBsZWFybmluZyB0b29scyBhcmUgbGVmdCB1bnJlZ3VsYXRlZCBhbmQgV2UgbmVlZCBsYXdzIGJlY2F1c2Ugbm8gcmVjb3Vyc2UuIA0KDQoNCg0KKldoYXQgZW1vdGlvbnMgZGlkIHlvdSBleHBlcmllbmNlIHdoaWxlIHdhdGNoaW5nPyoNCg0KQXMgSSB3YXMgd2F0Y2hpbmcgdGhpcyBmaWxtLCBJIHdhcyBzbGlnaHRseSBzaG9ja2VkLCBpbnRpbWlkYXRlZCwgYW5kIHNjYXJlZC4gVW50aWwgbm93LCB0ZWNobm9sb2d5IHdhcyBhbHdheXMgYSBmcmllbmRseSB0b29sIGZvciBtZSB0aGF0IGhhdmUgbWFkZSBteSBsaWZlIGVhc2llci4gSG93ZXZlciwgdGhyb3VnaCB0aGlzIHZpZGVvLCBJIGxlYXJuZWQgdGhhdCB0aGVzZSBiZW5lZml0cyBjb21lIHdpdGggY29zdHMgYW5kIHNvbWUgcGVvcGxlIGFyZSBhZmZlY3RlZCBtb3JlIHNldmVyZWx5IHRoYW4gb3RoZXJzLiBJdCBpcyB2ZXJ5IGltcG9ydGFudCB0byBjb25zaWRlciB0aGVzZSBwcm9ibGVtcyBhbmQgZGV2ZWxvcCByaWdodCByZWd1bGF0aW9ucyB0byBlbnN1cmUgZXZlcnlvbmUgaXMgYmVpbmcgdHJlYXRlZCBzb21ld2hhdCBmYWlybHkuIEkgYWxzbyBsZWFuZWQgdGhhdCB3ZSBhcmUgYmVpbmcgd2F0Y2hlZCAyNC83IGFuZCB0aGF0IHRlY2hub2xvZ3kgcHJvdmlkZXMgYW5kIGdvdmVybm1lbnRzIGFyZSBub3QgYmVpbmcgdHJhbnNwYXJlbnQgYWJvdXQgaXQuIA0KDQoNCg0KDQoNCg0K